﻿namespace Sai.Cecil
{
	using System;
	using Sai.Core.Introspection;
	using Mono.Cecil;

	/// <summary>
	/// Collection of extension methods used in discovering types in an assembly.
	/// </summary>
	public static class MonoCecilTypeExtensions
	{
		/// <summary>
		/// Does this property have a public getter.
		/// </summary>
		/// <param name="property">The property.</param>
		/// <returns>True if a public getter exists.</returns>
		public static bool HasPublicGetter(this PropertyDefinition property)
		{
			if (property.GetMethod != null)
			{
				return property.GetMethod.IsPublic;
			}

			return false;
		}

		/// <summary>
		/// Does this property have a public setter.
		/// </summary>
		/// <param name="property">The property.</param>
		/// <returns>True if a public setter exists.</returns>
		public static bool HasPublicSetter(this PropertyDefinition property)
		{
			if (property.SetMethod != null)
			{
				return property.SetMethod.IsPublic;
			}

			return false;
		}

		/// <summary>
		/// Is this type obsolete.
		/// </summary>
		/// <param name="typeDef">The type definition.</param>
		/// <returns>The obsolete marker object.</returns>
		public static ObsoleteMarker BuildObsoleteMarker(this TypeDefinition typeDef)
		{
			return BuildObsoleteMarker(typeDef.CustomAttributes);
		}

		/// <summary>
		/// Is this event obsolete.
		/// </summary>
		/// <param name="eventDef">The event definition.</param>
		/// <returns>The obsolete marker object.</returns>
		public static ObsoleteMarker BuildObsoleteMarker(this EventDefinition eventDef)
		{
			return BuildObsoleteMarker(eventDef.CustomAttributes);
		}

		/// <summary>
		/// Is this field obsolete.
		/// </summary>
		/// <param name="fieldDef">The field definition.</param>
		/// <returns>The obsolete marker object.</returns>
		public static ObsoleteMarker BuildObsoleteMarker(this FieldDefinition fieldDef)
		{
			return BuildObsoleteMarker(fieldDef.CustomAttributes);
		}

		/// <summary>
		/// Is this method obsolete.
		/// </summary>
		/// <param name="methodDef">The method definition.</param>
		/// <returns>The obsolete marker object.</returns>
		public static ObsoleteMarker BuildObsoleteMarker(this MethodDefinition methodDef)
		{
			return BuildObsoleteMarker(methodDef.CustomAttributes);
		}

		/// <summary>
		/// Is this property obsolete.
		/// </summary>
		/// <param name="propertyDef">The property definition.</param>
		/// <returns>The obsolete marker object.</returns>
		public static ObsoleteMarker BuildObsoleteMarker(this PropertyDefinition propertyDef)
		{
			return BuildObsoleteMarker(propertyDef.CustomAttributes);
		}

		/// <summary>
		/// Creates an obsolete marker by examining the attributes on an an element.
		/// </summary>
		/// <param name="attributes">Element's arguments.</param>
		/// <returns>The obsolete marker object.</returns>
		private static ObsoleteMarker BuildObsoleteMarker(Mono.Collections.Generic.Collection<CustomAttribute> attributes)
		{
			var obsoleteAttr = typeof(ObsoleteAttribute).ToString();
			bool obsolete = false;
			string message = string.Empty;

			foreach (var attr in attributes)
			{
				if (attr.AttributeType.FullName == obsoleteAttr)
				{
					obsolete = true;

					if (attr.HasConstructorArguments)
					{
						message = attr.ConstructorArguments[0].Value.ToString();
					}

					break;
				}
			}

			return new ObsoleteMarker(obsolete, message);
		}
	}
}
